package com.ijiuchuang.jc_localization.model;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;

import com.ijiuchuang.jc_localization.app.Config;
import com.ijiuchuang.jc_localization.bean.cache.DeviceListCache;
import com.ijiuchuang.jc_localization.bean.cache.TagCache;
import com.ijiuchuang.jc_localization.bean.event.BeaconTagData;
import com.ijiuchuang.jc_localization.bean.event.SocketGetInfoEvent;
import com.ijiuchuang.jc_localization.bean.lite.Map;
import com.ijiuchuang.jc_localization.bean.lite.Setts;
import com.ijiuchuang.jc_localization.bean.lite.Station;
import com.ijiuchuang.jc_localization.bean.lite.Tag;
import com.ijiuchuang.jc_localization.ble.BleFilter;
import com.ijiuchuang.jc_localization.contract.PositionContract;
import com.ijiuchuang.jc_localization.rx.MapOnBytesToBeaconTagData;
import com.ijiuchuang.jc_localization.rx.MapOnNewStringToBeaconTagData;
import com.ijiuchuang.jc_localization.tools.utils.HexUtil;
import com.ijiuchuang.jc_localization.tools.utils.Log;
import com.litesuits.orm.LiteOrm;
import com.litesuits.orm.db.assit.QueryBuilder;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * Created by Veev on 2016/6/30
 *
 * @link 384506557@qq.com
 */
public class PositionModel implements PositionContract.Model {

    private LiteOrm liteOrm = Config.liteOrm;
    private Map defaultMap = null;
    private List<Station> listStation = new ArrayList<>();
    private List<Socket> listSocket = new ArrayList<>();
    private HashMap<String, Socket> socketMap = new HashMap<>();

    @Override
    public void getMap(final Context context, Subscriber<Map> subscriber) {
        // 询问缓存
        Observable<Map> cache = Observable.create(new Observable.OnSubscribe<Map>() {
            @Override
            public void call(Subscriber<? super Map> subscriber) {
                if (defaultMap != null) {
                    subscriber.onNext(defaultMap);
                } else {
                    subscriber.onCompleted();
                }
            }
        });
        // 查找数据库
        Observable<Map> disk = Observable.create(new Observable.OnSubscribe<Map>() {
            @Override
            public void call(Subscriber<? super Map> subscriber) {
                QueryBuilder<Setts> equals = new QueryBuilder<>(Setts.class)
                        .whereEquals(Setts.COL_KEY, Setts.SETTINGS_MAP_SCENE);
                ArrayList<Setts> list = liteOrm.query(equals);
                long count = liteOrm.queryCount(Map.class);
                if (list.isEmpty()) {
                    if (count != 0) {
                        Map map = liteOrm.queryById(1, Map.class);
                        subscriber.onNext(map);
                    } else {
                        subscriber.onError(new Throwable("缓存没有"));
                    }
                } else {
                    Map map = liteOrm.queryById(list.get(0).getValueLong(), Map.class);
                    subscriber.onNext(map);
                }
            }
        });

        Observable.concat(cache, disk)
                .first()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);
    }

    @Override
    public void loadMap(final Context context, final Subscriber<Bitmap> subscriber) {
        getMap(context, new Subscriber<Map>() {
            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable e) {
                try {
                    String files[] = context.getAssets().list("image/map");
                    InputStream is = context.getAssets().open("image/map/" + files[0]);
                    Bitmap bitmap= BitmapFactory.decodeStream(is);
                    subscriber.onNext(bitmap);
                } catch (IOException e1) {
                    e1.printStackTrace();
                    subscriber.onError(e1);
                }
            }

            @Override
            public void onNext(Map map) {
                try {
                    InputStream is = context.getAssets().open("image/map/" + map.getMapUrl());
                    Bitmap bitmap= BitmapFactory.decodeStream(is);
                    subscriber.onNext(bitmap);
                } catch (IOException e) {
                    e.printStackTrace();
                    subscriber.onError(e);
                }
            }
        });
    }

    @Override
    public void loadStations(Context context, final Subscriber<List<Station>> subscriber) {
        getMap(context, new Subscriber<Map>() {
            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onNext(Map map) {
                QueryBuilder<Station> ss = new QueryBuilder<>(Station.class)
                        .whereEquals(Station.COL_MAP_ID, map.getId());
                List<Station> ll = liteOrm.query(ss);
                listStation = ll;
                // 初始化滤波后的信号强度 初始0
                for (Station station : ll) {
                    preRssiMap.put(station.getMac(), 0d);
                }
                subscriber.onNext(ll);
            }
        });
    }

    @Override
    public void closeSocket(String address) {
        Socket socket = socketMap.get(address);
        try {
            socket.close();
            socketMap.remove(address);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void closeSocket(Socket socket) {
        try {
            socket.close();
            listSocket.remove(socket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void closeAllSocket() {
        Iterator iterator1 = socketMap.entrySet().iterator();
        while (iterator1.hasNext()) {
            java.util.Map.Entry entry = (java.util.Map.Entry) iterator1.next();
            String address = (String) entry.getKey();
            Socket socket = (Socket) entry.getValue();
            try {
                socket.close();
                socketMap.remove(address);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public  List<Socket> getSocketList() {
        return listSocket;
    }

    @Override
    public void socketConnect(final Subscriber<SocketGetInfoEvent> subscriber) {
        Log.i("Snack is click");

        Observable
                .create(new Observable.OnSubscribe<Socket>() {
                    @Override
                    public void call(Subscriber<? super Socket> subscriber) {
                        ServerSocket server = null;
                        try {
                            server = new ServerSocket(8000);
                            server.setSoTimeout(Integer.MAX_VALUE);
                            while (true) {
                                Socket socket = server.accept();
                                subscriber.onNext(socket);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            subscriber.onError(e);
                        }
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .observeOn(Schedulers.immediate())
                .subscribe(new Subscriber<Socket>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i("onError in LocationModel socketConnect: " + e.toString());
                    }

                    @Override
                    public void onNext(Socket socket) {
                        Log.i("onNext: " + socket);
                        // 缓存所有的socket连接
                        socketMap.put(socket.getInetAddress().toString(), socket);
                        listSocket.add(socket);
                        SocketProcess sp = new SocketProcess(socket, subscriber);
                        sp.start();
                    }
                });
    }

    private class SocketProcess extends Thread {
        private Socket socket;
        private DataInputStream dataInputStream = null;
        private Subscriber<SocketGetInfoEvent> subscriber;

        public SocketProcess(Socket socket, Subscriber<SocketGetInfoEvent> subscriber) {
            this.socket = socket;
            this.subscriber = subscriber;
        }

        @Override
        public void run() {
            super.run();
            // TODO: 2016/6/28 迷之缩进，用RxJava改进之
            Observable
                    .create(new Observable.OnSubscribe<DataInputStream>() {
                        @Override
                        public void call(Subscriber<? super DataInputStream> subscriber) {
                            try {
                                dataInputStream = new DataInputStream(socket.getInputStream());
                                while (true) {
                                    if (dataInputStream.available() > 0) {
                                        subscriber.onNext(dataInputStream);
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    })
                    .map(new Func1<DataInputStream, byte[]>() {
                        @Override
                        public byte[] call(DataInputStream dataInputStream) {
                            byte[] msg = new byte[0];
                            try {
                                msg = new byte[dataInputStream.available()];
                                dataInputStream.read(msg, 0, dataInputStream.available());
                            } catch (Exception exception) {
                                exception.printStackTrace();
                            }
                            return msg;
                        }
                    })
                    .filter(new Func1<byte[], Boolean>() {
                        @Override
                        public Boolean call(byte[] bytes) {
                            return bytes.length > 9;
                        }
                    })
                    .map(new Func1<byte[], SocketGetInfoEvent>() {
                        @Override
                        public SocketGetInfoEvent call(byte[] bytes) {
                            return new SocketGetInfoEvent(bytes);
                        }
                    })
                    .subscribe(subscriber);
        }
    }

    private BeaconTagData data0 = new BeaconTagData();
    // 滤波后rssi值
    private HashMap<String, Double> preRssiMap = new HashMap<>();
    static PointF posXY = new PointF();

    @Override
    public void locationProcess(final SocketGetInfoEvent socketGetInfoEvent, final Subscriber<TagCache> subscriber) {
        // 将socket收到的bytes转为Tag
        Observable.just(socketGetInfoEvent.getData())
                .filter(new Func1<byte[], Boolean>() {
                    @Override
                    public Boolean call(byte[] bytes) {
                        return bytes.length % 9 == 0;
                    }
                })
                .map(new Func1<byte[], String>() {
                    @Override
                    public String call(byte[] bytes) {
                        return HexUtil.encodeHexStr(bytes);
                    }
                })
                //.map(MapOnNewBytesToBeaconTagData.getInstance())
                .flatMap(new Func1<String, Observable<String>>() {
                    @Override
                    public Observable<String> call(String msgStr) {
                        // 去掉字符串中的 0d0a
                        String regex = "0d0a";
                        String processStr = msgStr.replaceAll(regex, "");
                        int len = processStr.length();

                        // 长度为18， 等长分割
                        String regexSplit = "(?<=\\G.{18})";
                        String strArr[]  = processStr.split(regexSplit);
                        return Observable.from(strArr);
                    }
                })
                .map(MapOnNewStringToBeaconTagData.getInstance())
                .filter(new Func1<BeaconTagData, Boolean>() {
                    @Override
                    public Boolean call(BeaconTagData beaconTagData) {
                        return beaconTagData.rssi < 0;
                    }
                })
                .subscribe(new Action1<BeaconTagData>() {
                    @Override
                    public void call(BeaconTagData beaconTagData) {
                        data0 = beaconTagData;
                        concat(data0, subscriber);
                    }
                });
    }

    private void concat(final BeaconTagData data, final Subscriber<TagCache> subscriber) {
        Observable<TagCache> cache = Observable.create(new Observable.OnSubscribe<TagCache>() {
            @Override
            public void call(Subscriber<? super TagCache> subscriber) {
                if (DeviceListCache.getInstance().isCached(data.tagUuid)) {
                    subscriber.onNext(DeviceListCache.getInstance().get(data.tagUuid));
                } else {
                    subscriber.onCompleted();
                }
            }
        });

        Observable<TagCache> database = Observable.create(new Observable.OnSubscribe<TagCache>() {
            @Override
            public void call(Subscriber<? super TagCache> subscriber) {
                QueryBuilder<Tag> equals = new QueryBuilder<>(Tag.class)
                        .whereEquals(Tag.COL_MAC, data.tagUuid);
                if (liteOrm.query(equals) != null) {
                    ArrayList<Tag> list = liteOrm.query(equals);
                    if (list.size() != 0) {
                        TagCache tagCache = new TagCache(list.get(0).getMac(), list.get(0).getName(), list.get(0).isShow());
                        DeviceListCache.getInstance().put(data.tagUuid, tagCache);
                        subscriber.onNext(tagCache);
                    } else {
                        subscriber.onCompleted();
                    }
                } else {
                    subscriber.onCompleted();
                }
            }
        });

        Observable<TagCache> create = Observable.create(new Observable.OnSubscribe<TagCache>() {
            @Override
            public void call(Subscriber<? super TagCache> subscriber) {
                TagCache tagCache = new TagCache(data.tagUuid, "", false);
                Tag tag = new Tag(data.tagUuid.trim(), "");
                liteOrm.save(tag);
                Log.i("发现新设备： " + data.tagUuid);
                subscriber.onNext(tagCache);
            }
        });

        Observable.concat(cache, database, create)
                .first()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .filter(new Func1<TagCache, Boolean>() {
                    @Override
                    public Boolean call(TagCache tagCache) {
                        return tagCache.isShow;
                    }
                })
                .subscribe(new Subscriber<TagCache>() {
                    @Override
                    public void onCompleted() {
                        // Log.i("onCompleted");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i("error: " + e.toString());
                    }

                    @Override
                    public void onNext(TagCache tagCache) {
                        for (Station station : listStation) {
                            if (data.beaconUuid.equals(station.getMac())) {
                                double preRssi = preRssiMap.get(station.getMac());
                                preRssi = BleFilter.KalmanFiltering(preRssi, data.rssi);
                                preRssiMap.put(station.getMac(), preRssi);
                                if (preRssi > (0 - station.getLimit())) {
                                    posXY.x = station.getX();
                                    posXY.y = station.getY();
                                    tagCache.pointF = posXY;
                                    Log.i(String.format("我在%d号点", station.getNum()));
                                    subscriber.onNext(tagCache);
                                }
                            }
                        }
                    }
                });
    }

}
